import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

/**
 * Class to display graphical user interface of the simulator.
 * CS 149 Project, Phase 1
 * @author Timmy Yee
 * @author Miaoer Yu
 */

public class Display extends JPanel
{
    private JTextArea trace; // text area to display trace
    private JLabel turnaround; // turnaround time
    private JLabel waiting; // waiting time
    private JLabel response; // response time
    
    /**
     * Creates a button on the user interface.
     * @param label - label for the button
     * @param al - listener for the button
     * @return button for the user interface
     */
    private JButton createButton(String label, ActionListener al)
    {
        JButton button = new JButton(label);
        button.addActionListener(al);
        return button;
    }

    /**
     * Creates a combo box on the user interface.
     * @param s - list of options to display in combo box
     * @param i - default selected option
     * @param al - listener for the combo box
     * @return combo box for the user interface
     */
    private JComboBox createComboBox(String[] s, int i, ActionListener al)
    {
        JComboBox cb = new JComboBox(s);
        cb.setSelectedIndex(i);
        cb.addActionListener(al);
        return cb;
    }

    /**
     * Create a spinner box on the user interface.
     * @param initial - initial value on the spinner
     * @param cl - listener for the spinner
     * @return spinner for the user interface
     */
    private JSpinner createSpinner(int initial, ChangeListener cl)
    {
        JSpinner s = new JSpinner(new SpinnerNumberModel(initial,
                                  0, Integer.MAX_VALUE, 1));
        s.addChangeListener(cl);
        return s;
    }

    /**
     * Creates a text area.
     * @param font - font for text in the text area
     * @return the text area
     */
    private JTextArea createArea(Font font)
    {
        JTextArea area = new JTextArea(20, 60);
        area.setEditable(false);
        area.setFont(font);
        return area;
    }

    /**
     * Creates the top panel of the user interface.
     * @return the top panel of the GUI
     */
    private JPanel createTop()
    {
        JPanel panel = new JPanel();

        trace = createArea(new Font("monospaced", Font.PLAIN, 12));
        panel.add(new JScrollPane(trace));

        return panel;
    }

    /**
     * Creates the center panel of the user interface
     * @return the center panel of the GUI
     */
    private JPanel createCenter()
    {
        JPanel panel = new JPanel(new GridLayout(3, 2));

        panel.add(new JLabel("Average turnaround time: ", JLabel.RIGHT));
        turnaround = new JLabel("N/A");
        panel.add(turnaround);

        panel.add(new JLabel("Average waiting time: ", JLabel.RIGHT));
        waiting = new JLabel("N/A");
        panel.add(waiting);

        panel.add(new JLabel("Average response time: ", JLabel.RIGHT));
        response = new JLabel("N/A");
        panel.add(response);

        return panel;
    }

    /**
     * Creates the bottom panel and frame of the user interface
     * @param frame - the frame of the GUI
     * @return the bottom panel of the GUI
     */
    private JPanel createBottom(final JFrame frame)
    {
        JPanel panel = new JPanel(new GridLayout(2, 5));
        final JFileChooser chooser = new JFileChooser(".");
        String[] algorithms = {
            "Preemptive Priority",
            "Non-preemptive Priority",
            "Round Robin",
        };

        panel.add(createComboBox(algorithms, 2, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                switch (((JComboBox) e.getSource()).getSelectedIndex()) {
                case 0:
                    Scheduler.setAlgorithm(Algorithm.PREEMPT);
                    return;
                case 1:
                    Scheduler.setAlgorithm(Algorithm.NON_PREEMPT);
                    return;
                case 2:
                    Scheduler.setAlgorithm(Algorithm.ROUND_ROBIN);
                    return;
                }
            }
        }));

        panel.add(new JLabel("Time slice: "));
        panel.add(createSpinner(Processor.getTimeSlice(),
                                new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                JSpinner s = (JSpinner) e.getSource();
                Processor.setTimeSlice((Integer) s.getValue());
            }
        }));

        panel.add(new JLabel("Context switch: "));
        panel.add(createSpinner(Processor.getContextSwitch(),
                                new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                JSpinner s = (JSpinner) e.getSource();
                Processor.setContextSwitch((Integer) s.getValue());
            }
        }));

        panel.add(new JLabel("Display queues every: "));
        panel.add(createSpinner(Processor.getDisplayInterval(),
                                new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                JSpinner s = (JSpinner) e.getSource();
                Processor.setDisplayInterval((Integer) s.getValue());
            }
        }));

        panel.add(new JLabel("Pause at: "));
        panel.add(createSpinner(Processor.getPauseInterval(),
                                new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                JSpinner s = (JSpinner) e.getSource();
                Processor.setPauseInterval((Integer) s.getValue());
            }
        }));

        panel.add(createButton("Load Test", new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                trace.setText("");
                chooser.showOpenDialog(frame);
                Simulator.readTestData(chooser.getSelectedFile());
            }
        }));

        return panel;
    }

    /**
     * Appends the trace.
     * @param raw - the contents to append to the trace.
     */
    public void append(String raw)
    {
        trace.append(raw);
    }

    /**
     * Appends trace log with formatted time.
     * @param time - time to be formatted
     * @param log - log to append
     */
    public void appendLog(double time, String log)
    {
        trace.append(String.format("%.1f\t" + log, time));
    }

    /**
     * Updates the turnaround field displayed on the user interface.
     * @param time - new value for turnaround time
     */
    public void updateTurnaroundField(double time)
    {
        turnaround.setText(String.format("%.1f", time));
    }

    
    /**
     * Updates the waiting time field displayed on the user interface.
     * @param time - new value for waiting time
     */
    public void updateWaitingField(double time)
    {
        waiting.setText(String.format("%.1f", time));
    }

    /**
     * Updates the response time field displayed on the user interface.
     * @param time - new value for response time
     */
    public void updateResponseField(double time)
    {
        response.setText(String.format("%.1f", time));
    }

    /**
     * Constructs the user interface for the simulator.
     * @param frame - the display frame
     */
    public Display(JFrame frame)
    {
        super(new BorderLayout(8, 8));

        add(createTop(), BorderLayout.PAGE_START);
        add(createCenter(), BorderLayout.CENTER);
        add(createBottom(frame), BorderLayout.PAGE_END);
    }
}

